/****************************************************************************
 * Copyright (C) 2009-2010 SciTouch LLC
 * 
 * This file is part of Indigo toolkit.
 * 
 * This file may be distributed and/or modified under the terms of the
 * GNU General Public License version 3 as published by the Free Software
 * Foundation and appearing in the file LICENSE.GPL included in the
 * packaging of this file.
 * 
 * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
 * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
 ***************************************************************************/

#ifndef __reaction_h__
#define __reaction_h__

#include "base_cpp/array.h"
#include "base_cpp/obj_array.h"
#include "molecule/molecule.h"
#include "molecule/molecule_neighbourhood_counters.h"

// Stereo changes during reaction
enum
{
   STEREO_UNMARKED = 0,
   STEREO_INVERTS  = 1,
   STEREO_RETAINS  = 2
};


// Reacting centers
enum
{
   RC_NOT_CENTER     = -1,
   RC_UNMARKED       =  0,
   RC_CENTER         =  1,
   RC_UNCHANGED      =  2,
   RC_MADE_OR_BROKEN =  4,
   RC_ORDER_CHANGED  =  8,
   RC_TOTAL          = 16
};

class Reaction {
public:
   enum
   {
      REACTANT = 1,
      PRODUCT = 2,
      CATALYST = 4
   };

   explicit Reaction();

   int begin() const                   { return _nextElement(REACTANT | PRODUCT | CATALYST, -1); }
   int next(int index) const           { return _nextElement(REACTANT | PRODUCT | CATALYST, index); }
   int end() const                     { return _indexes.size(); }
   int count() const                   { return end(); }

   int reactantBegin() const           { return _nextElement(REACTANT, -1); }
   int reactantNext(int index) const   { return _nextElement(REACTANT, index); }
   int reactantEnd() const             { return _indexes.size(); }

   int productBegin() const            { return _nextElement(PRODUCT, -1); }
   int productNext(int index) const    { return _nextElement(PRODUCT, index); }
   int productEnd() const              { return _indexes.size(); }

   int catalystBegin() const            { return _nextElement(CATALYST, -1); }
   int catalystNext(int index) const    { return _nextElement(CATALYST, index); }
   int catalystEnd() const              { return _indexes.size(); }
                                                                     
   int sideBegin (int side) const            { return _nextElement(side, -1); }
   int sideNext (int side, int index) const    { return _nextElement(side, index); }
   int sideEnd () const              { return _indexes.size(); }
   int sideCount (int side) const;

   int getSideType(int index) const {return _indexes[index]; }

   void clear();

   Molecule&         getMolecule(int index)             { return _allMolecules.at(index); }
   const Molecule&   getMolecule(int index) const       { return _allMolecules.at(index); }

   int getAAM(int index, int atom) const             { return _allFlags[index].atomAtomMapping.at(atom);}
   int getReactingCenter(int index, int bond) const  { return _allFlags[index].reactingCenters.at(bond);}
   int getInversion(int index, int atom) const       { return _allFlags[index].inversionNumbers.at(atom);}
   int getExactChange(int index, int atom) const     { return _allFlags[index].queryExactChanges.at(atom);}

   const Array<int>& getAAMArray(int index) const            { return _allFlags[index].atomAtomMapping;}
   const Array<int>& getReactingCenterArray(int index) const { return _allFlags[index].reactingCenters;}
   const Array<int>& getInversionArray(int index)  const     { return _allFlags[index].inversionNumbers;}
   const Array<int>& getExactChangeArray(int index) const    { return _allFlags[index].queryExactChanges;}

   Array<int>& getAAMArray(int index)             { return _allFlags[index].atomAtomMapping;}
   Array<int>& getReactingCenterArray(int index)  { return _allFlags[index].reactingCenters;}
   Array<int>& getInversionArray(int index)       { return _allFlags[index].inversionNumbers;}
   Array<int>& getExactChangeArray(int index)     { return _allFlags[index].queryExactChanges;}

   int reactantsCount() const { return _reactantCount; }
   int productsCount() const { return _productCount; }
   int catalystCount() const { return _catalystCount; }

   int addReactantMolecule();
   int addProductMolecule();
   int addCatalystMolecule();

   int addReactantMoleculeCopy(const Molecule& mol, Array<int>* mapping, Array<int> *inv_mapping);
   int addProductMoleculeCopy(const Molecule& mol, Array<int>* mapping, Array<int> *inv_mapping);
   int addCatalystMoleculeCopy(const Molecule& mol, Array<int>* mapping, Array<int> *inv_mapping);

   int findMoleculeSideIdx(const Molecule* qmol) const;
   int findMoleculeIdx(const Molecule* qmol) const;
   int findAamNumber(const Molecule* qmol, int atom_number) const;
   int findInversionNumber(const Molecule* qmol, int atom_number) const;
   int findReactingCenter(const Molecule* qmol, int bond_number) const;
   int findExactChange(const Molecule* qmol, int atom_number) const;

   int findAtomByAAM (int mol_idx, int aam) const;

   void clearAAM();
   void aromatizeBonds();
   void dearomatizeBonds();
   void aromatizeQueryBonds();
   void calcImplicitHydrogens(bool allow_bad);
   void calcConnectivity();
   void findBondsInRings();
   bool isAllConnected() const;
   void makeTransposedForSubstructure(const Reaction &other);
   void markStereocenterBonds();

   static bool haveCoord (const Reaction &reaction);

   void cloneReaction(const Reaction& other, ObjArray< Array<int> >* mappings, ObjArray< Array<int> >* inv_mappings);

   static void saveBondTypes(const Reaction& reaction, ObjArray< Array<int> > &bond_types);
   static void loadBondTypes(Reaction& reaction, ObjArray< Array<int> > &bond_types);

   Array<char> name;

   DEF_ERROR("reaction");

private:
   class _ReactionFlags {
   public:
      _ReactionFlags() {
      }
      Array<int> atomAtomMapping;
      Array<int> reactingCenters;
      Array<int> inversionNumbers;
      Array<int> queryExactChanges;

      void init(const Molecule& mol) { init(mol.vertexEnd(), mol.edgeEnd()); }
      void init(int vertex_count, int edge_count);

   private:
      _ReactionFlags(const _ReactionFlags&);//no implicit copy
   };

   ObjArray<Molecule> _allMolecules;
   ObjArray<_ReactionFlags> _allFlags;
   Array<int> _indexes;

   int _reactantCount;
   int _productCount;
   int _catalystCount;

   int _nextElement(int type, int index) const;
   int _addMolecule(int type, const Molecule* mol, Array<int>* mapping, Array<int> *inv_mapping);
   
   void _transposeMoleculeForSubstructure (int index, Array<int> &transposition) const;
   static int _compare(const int &i1, const int &i2, const void *c);

   struct _SortingContext {
      explicit _SortingContext(const Molecule &mol, const Array<int> &r) : m(mol), rdata(r) {
      }

      const Molecule &m;
      const Array<int> &rdata;
   };

   Reaction(const Reaction&);//no implicit copy
};

struct ReactionHighlighting
{
   void clear ();
   void init (const Reaction &reaction);
   void nondestructiveInit (const Reaction &reaction);
   const GraphHighlighting& getGraphHighlighting (int index) const { return _graphHighlightings[index]; }
         GraphHighlighting& getGraphHighlighting (int index)       { return _graphHighlightings[index]; }
   int getCount() const {return _graphHighlightings.size();}
private:
   ObjArray<GraphHighlighting> _graphHighlightings;
};

class ReactionAtomNeighbourhoodCounters
{
public:
   void calculate (const Reaction &reac, bool is_query);
   const MoleculeAtomNeighbourhoodCounters & getCounters (int idx) const { return _counters[idx]; }

private:
   ObjArray<MoleculeAtomNeighbourhoodCounters> _counters;
};

#endif


